package com.zyd.shiro.business.shiro.realm;

import java.util.List;

import javax.annotation.Resource;

import com.zyd.shiro.business.enums.UserStatusEnum;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.zyd.shiro.business.consts.SessionConst;
import com.zyd.shiro.business.entity.Resources;
import com.zyd.shiro.business.entity.Role;
import com.zyd.shiro.business.entity.User;
import com.zyd.shiro.business.service.SysResourcesService;
import com.zyd.shiro.business.service.SysRoleService;
import com.zyd.shiro.business.service.SysUserService;
import com.zyd.shiro.framework.exception.ZhydException;

/** 
 * 权限校验核心类; 由于使用了单点登录，所以无需再进行身份认证 只需要授权即可 
 * 
 * @author chhliu 
 */
public class MyShiroRealm extends CasRealm { 
  
  @Resource
  private SysUserService userService;
  @Resource
  private SysResourcesService resourcesService;
  @Resource
  private SysRoleService roleService;
  
  /** 
   * 1、CAS认证 ,验证用户身份 
   * 2、将用户基本信息设置到会话中,方便获取 
   * 3、该方法可以直接使用CasRealm中的认证方法，此处仅用作测试 
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { 
  
    // 调用父类中的认证方法，CasRealm已经为我们实现了单点认证。 
    AuthenticationInfo authc = super.doGetAuthenticationInfo(token); 
  
    // 获取登录的账号，cas认证成功后，会将账号存起来 
    String account = (String) authc.getPrincipals().getPrimaryPrincipal(); 
    User user = userService.getByUserName(account);
    if (user == null) {
        throw new UnknownAccountException("账号不存在！");
    }else if (UserStatusEnum.DISABLE.getCode().equals(user.getStatus())) {
        throw new LockedAccountException("帐号已被锁定，禁止登录！");
    }
    // 将用户信息存入session中,方便程序获取,此处可以将根据登录账号查询出的用户信息放到session中 
    // 注：User必须实现序列化
    SecurityUtils.getSubject().getSession().setAttribute(SessionConst.USER_SESSION_KEY, user);
    return new SimpleAuthenticationInfo(user, authc.getCredentials(), getName());
  }
  
  /** 
   * 此方法调用 hasRole,hasPermission的时候才会进行回调.
   * 进行鉴权但缓存中无用户的授权信息时才调用. 
   * 
   * 权限信息.(授权): 1、如果用户正常退出，缓存自动清空； 2、如果用户非正常退出，缓存自动清空； 
   * 3、如果我们修改了用户的权限，而用户不退出系统，修改的权限无法立即生效。 （需要手动编程进行实现；放在service进行调用） 
   * 在权限修改后调用realm中的方法，realm已经由spring管理，所以从spring中获取realm实例， 调用clearCached方法； 
   * :Authorization 是授权访问控制，用于对用户进行的操作授权，证明该用户是否允许进行当前操作，如访问某个链接，某个资源文件等。 
   * 
   * @param principals 
   * @return 
   */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 
    System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()"); 
  
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 
    User user = (User) SecurityUtils.getSubject().getPrincipal();
    Long userId = user.getId();

    // 赋予角色
    List<Role> roleList = roleService.listRolesByUserId(userId);
    for (Role role : roleList) {
        info.addRole(role.getName());
    }

    // 赋予权限
    List<Resources> resourcesList = resourcesService.listByUserId(userId);
    if (!CollectionUtils.isEmpty(resourcesList)) {
        for (Resources resources : resourcesList) {
            String permission = resources.getPermission();
            System.out.println(resources.getName() + "   " + permission);
            if (!StringUtils.isEmpty(permission)) {
                info.addStringPermission(permission);
            }
        }
    }
  
    return info; 
  } 
} 
